/* File: ast_decl.cc
 * -----------------
 * Implementation of Decl node classes.
 */
#include "ast_decl.h"
#include "ast_type.h"
#include "ast_stmt.h"
#include "codegen.h"
        
         
Decl::Decl(Identifier *n) : Node(*n->GetLocation()) {
    Assert(n != NULL);
    (id=n)->SetParent(this); 
    scope = new Scope;
}


VarDecl::VarDecl(Identifier *n, Type *t) : Decl(n) {
    Assert(n != NULL && t != NULL);
    (type=t)->SetParent(this);
}

int VarDecl::getSpace() {
	return CodeGenerator::VarSize;
}

ClassDecl::ClassDecl(Identifier *n, NamedType *ex, List<NamedType*> *imp, List<Decl*> *m) : Decl(n) {
    // extends can be NULL, impl & mem may be empty lists but cannot be NULL
    Assert(n != NULL && imp != NULL && m != NULL);     
    extends = ex;
    if (extends) extends->SetParent(this);
    (implements=imp)->SetParentAll(this);
    (members=m)->SetParentAll(this);
}

NamedType* ClassDecl::getType() {
	return new NamedType(id);
}

void ClassDecl::useScope() {
	int n = members->NumElements();
	
	for(int i = 0; i < n; ++i) {
		scope->insertDecl(members->Nth(i));
	}
	
	for(int i = 0; i < n; ++i) {
		members->Nth(i)->useScope();
	}
}

void ClassDecl::locationSet() {
	int offset = CodeGenerator::OffsetToFirstField;
	int vOffset = CodeGenerator::OffsetToFirstMethod;
	
	if(extends != NULL) {
		Decl *d = Program::globalScope->map->Lookup(extends->GetName());
		Assert(d != NULL);
		offset += d->getSpace();
		vOffset += d->getVSpace();
	}
	
  for (int i = 0, n = members->NumElements(); i < n; ++i) {
      VarDecl *d = dynamic_cast<VarDecl*>(members->Nth(i));
      if (d != NULL) {
     		d->setOffset(offset);
    		offset += d->getSpace();
		  }
	}

  for (int i = 0, n = members->NumElements(); i < n; ++i) {
      FnDecl *d = dynamic_cast<FnDecl*>(members->Nth(i));
      if (d == NULL) {
				continue;
			}
      d->setCheck(true);
      d->setVOffset(vOffset);
      vOffset += CodeGenerator::VarSize;
  }

  for (int i = 0, n = members->NumElements(); i < n; ++i) {
      std::string p;
      p += getName();
      p += ".";
      members->Nth(i)->addNamePrefix(p.c_str());
  }
}

int ClassDecl::getSpace() {
        int b = 0;
	if ( extends != NULL) {
                Decl *decl = Program::globalScope->map->Lookup(extends->GetName());
                Assert(decl != NULL);
                b += decl->getSpace();
        }
        for ( int i = 0; i < members->NumElements(); i++ ) {
                b += members->Nth(i)->getSpace();
        }
        return b;
}

int ClassDecl::getVSpace() {
        int v = 0;
        if ( extends != NULL ) {
                Decl *decl = Program::globalScope->map->Lookup(extends->GetName());
                Assert(decl != NULL);
                v = v + decl->getVSpace();
        }
        for ( int i = 0; i < members->NumElements(); i++ ) {
                v = v + members->Nth(i)->getVSpace();
        }
        return v;
}

Location* ClassDecl::codeGen(CodeGenerator *gen) {
        for ( int i = 0; i < members->NumElements(); i++ ) {
                members->Nth(i)->codeGen(gen);
        }
        List<const char*> *l = new List<const char*>;
        List<FnDecl*> *d = getDecls();
        for( int i = 0; i < d->NumElements(); i++ ) {
                l->Append(d->Nth(i)->getName());
        }
        gen->GenVTable(getName(),l);
        return NULL;
}

List<FnDecl*>* ClassDecl::getDecls() {
        List<FnDecl*> *decls = new List<FnDecl*>;
        if ( extends != NULL) {
                Decl* decl = Program::globalScope->map->Lookup(extends->GetName());
                ClassDecl *cl = dynamic_cast<ClassDecl*>(decl);
                Assert(cl != NULL);
                List<FnDecl*> *ext = cl->getDecls();
                for( int i = 0; i < ext->NumElements(); i++ ) {
                        decls->Append(ext->Nth(i));
                }
        }
        for ( int i = 0; i < members->NumElements(); i++ ) {
                FnDecl *d = dynamic_cast<FnDecl*>(members->Nth(i));
                if ( d == NULL ) {
                        continue;
                }
                for ( int j = 0; j < decls->NumElements(); j++ ) {
                        if ( strcmp(decls->Nth(j)->getName(), d->getName() ) == 0 ) {
                                decls->RemoveAt(j);
                                decls->InsertAt(d,j);
                        }
                }
        }

        for ( int i = 0; i < members->NumElements(); i++ ) {
                FnDecl *d = dynamic_cast<FnDecl*>(members->Nth(i));
                if ( d == NULL ) {
                        continue;
                }
                decls->Append(d);
        }
        return decls;
}

InterfaceDecl::InterfaceDecl(Identifier *n, List<Decl*> *m) : Decl(n) {
    Assert(n != NULL && m != NULL);
    (members=m)->SetParentAll(this);
}

void InterfaceDecl::useScope() {
        for ( int i = 0; i < members->NumElements(); i++ ) {
                scope->insertDecl(members->Nth(i));
        }
        for ( int i = 0; i < members->NumElements(); i++ ) {
                members->Nth(i)->useScope();
        }
}
	
FnDecl::FnDecl(Identifier *n, Type *r, List<VarDecl*> *d) : Decl(n) {
    Assert(n != NULL && r!= NULL && d != NULL);
    (returnType=r)->SetParent(this);
    (formals=d)->SetParentAll(this); 
    body = NULL;
    Node* p = this->GetParent();
    ClassDecl* classDecl = dynamic_cast<ClassDecl*>(p);
    label = new std::string(getName());
    if(*label != "main") {
            label->insert(0,"____");
    }
    checkMethod = false;
}

const char* FnDecl::getLabel() {
        return label->c_str();
}

void FnDecl::SetFunctionBody(Stmt *b) { 
    (body=b)->SetParent(this);
}

bool FnDecl::checkReturn() {
        return returnType != Type::voidType;
}

void FnDecl::useScope() {
        for(int i = 0; i < formals->NumElements(); i++ ) {
                scope->insertDecl(formals->Nth(i));
        }
        for(int i = 0; i < formals->NumElements(); i++ ) {
                formals->Nth(i)->useScope();
        }
        if ( body != NULL ) {
                body->useScope();
        }
}

Location *FnDecl::codeGen(CodeGenerator *gen) {
        int o = CodeGenerator::OffsetToFirstParam;
        if ( checkMethod ) {
                o += CodeGenerator::VarSize;
        }
        for (int i = 0; i < formals->NumElements(); i++ ) {
                VarDecl *decl = formals->Nth(i);
                Location *l = new Location(fpRelative, o, decl->getName());
                decl->setLocation(l);
                o += decl->getSpace();
        }
        if ( body != NULL ) {
                gen->GenLabel(getLabel());

								int size = body->getSpace();
                gen->GenBeginFunc()->SetFrameSize(size);

                body->codeGen(gen);
                gen->GenEndFunc();
        }
        return NULL;
}

int FnDecl::getVSpace() {
        return CodeGenerator::VarSize;
}

void FnDecl::addNamePrefix(const char *l) {label->insert(0,l); }

